#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "include/pf_xml.h"
#include "include/pf_internal.h"

typedef enum pf_xml_nodetype
{
    PF_XML_NODE_EMPTY = 0,
    PF_XML_NODE_ATTRIBUTE,  
    PF_XML_NODE_ELEMENT,
    PF_XML_NODE_TEXT,
    PF_XML_NODE_CLEAR
} pf_xml_nodetype_e;

struct pf_xml_element
{
    char*                   name;           /* Element name              */
    int                     count;          /* Num of child nodes        */
    int                     max;            /* Num of allocated nodes    */
    struct pf_xml_node*     entries;        /* Array of child nodes      */
};

struct pf_xml_text
{
    char*   value;
};

struct pf_xml_clear
{
    char*   open_tag;
    char*   value;
    char*   close_tag;
};

struct pf_xml_attribute
{
    char*   name;
    char*   value;
};

typedef enum pf_xml_error
{
    PF_XML_ERR_NONE = 0,
    PF_XML_ERR_EMPTY,
    PF_XML_ERR_FIRST_NOT_START_TAG,
    PF_XML_ERR_MISS_TAG_NAME,
    PF_XML_ERR_MISS_END_TAG_NAME,
    PF_XML_ERR_UNMATCH_QUOTE,
    PF_XML_ERR_UNMATCH_END_TAG,
    PF_XML_ERR_UNEXPECTED_TOKEN,
    PF_XML_ERR_INVALID_TAG,
    PF_XML_ERR_INVALID_ATTR,
    PF_XML_ERR_NO_ELEMENTS
} pf_xml_error_e;

typedef struct pf_xml_node
{
    pf_xml_nodetype_e type;
    union
    {
        struct pf_xml_attribute*    attrib;
        struct pf_xml_element*      element;
        struct pf_xml_text*         text;
        struct pf_xml_clear*        clear;
    } node;
} pf_xml_node_t;

typedef struct pf_xml_results
{
    int             line;
    int             column;
    pf_xml_error_e  err_code;
} pf_xml_results_t;


typedef struct pf_xml_declaration
{
    pf_xml_attribute_t* version;
    pf_xml_attribute_t* encoding;
    pf_xml_attribute_t* standalone;

} pf_xml_declaration_t;

struct pf_xml
{
    pf_xml_declaration_t* decalare;
    pf_xml_element_t* root;
};

typedef enum pf_xml_status
{
    PF_XML_STATUS_INSIDE_TAG = 0,
    PF_XML_STATUS_OUTSIDE_TAG
} pf_xml_status_e;

typedef enum pf_xml_attrib
{
    PF_XML_ATTRIB_NAME = 0,
    PF_XML_ATTRIB_EQUALS,
    PF_XML_ATTRIB_VALUE
} pf_xml_attrib_e;

typedef enum pf_xml_token_type
{
    PF_XML_TOKEN_TEXT = 0,
    PF_XML_TOKEN_QUOTED_TEXT,
    PF_XML_TOKEN_TAG_START,         /* "<"   */
    PF_XML_TOKEN_TAG_END,           /* "</"  */
    PF_XML_TOKEN_CLOSE_TAG,         /* ">"   */
    PF_XML_TOKEN_EQUALS,            /* "="   */
    PF_XML_TOKEN_DECLARATION,       /* "<?"  */
    PF_XML_TOKEN_SHORT_HAND_CLOSE,   /* "/>"  */
    PF_XML_TOKEN_CLEAR,
    PF_XML_TOKEN_ERROR
} pf_xml_token_type_e;

typedef struct pf_xml_clear_tag
{
    char*   open;
    char*   close;
} pf_xml_clear_tag_t;

typedef struct pf_xml_next_token
{
    pf_xml_clear_tag_t*     clear;
    const char*             str_begin;
} pf_xml_next_token_t;

typedef struct pf_xml_buffer
{
    const char*         xml;
    int                 index;
    pf_xml_error_e      err_code;
    const char*         end_tag;
    int                 end_tag_len;
    const char*         new_element;
    int                 new_element_len;
    int                 first;
    pf_xml_clear_tag_t* clear_tags;
} pf_xml_buffer_t;

#define GROWBY          (5)
#define INDENTCHAR      (' ')
#define LENSTR(str)     (str ? strlen(str) : 0)
#define VERSION         ("version")
#define ENCODING        ("encoding")
#define STANDALONE      ("standalone")


/*********************************************************************************************
Function Name:  get_clear_tags
Description  :  Get the clear array
Inputs       :
Outputs      :  return value            :   The pointer of clear array 
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static pf_xml_clear_tag_t* get_clear_tags(void);

/*********************************************************************************************
Function Name:  enum_text
Description  :  Enum the text object by the current element
Inputs       :
Outputs      :  return value            :   The pointer of text object
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static pf_xml_text_t* enum_text(pf_xml_element_t* entry, int* idx);

/*********************************************************************************************
Function Name:  enum_clear
Description  :  Enum the clear object by the current element
Inputs       :
Outputs      :  return value            :   The pointer of clear object
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static pf_xml_clear_t* enum_clear(pf_xml_element_t* entry, int* idx);

/*********************************************************************************************
Function Name:  init_element
Description  :  Init a new element
Inputs       :
Outputs      :  
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static void init_element(pf_xml_element_t* entry, const char* name, int need_malloc);

/*********************************************************************************************
Function Name:  attach_nodes
Description  :  attach the old nodes to the dst nodes
Inputs       :
Outputs      :  
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static void attach_nodes(pf_xml_node_t* dst, pf_xml_node_t* src, int num);

/*********************************************************************************************
Function Name:  alloc_nodes
Description  :  Allocate new nodes when reach the max count
Inputs       :
Outputs      :  return value            :  0 success, other fail 
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int alloc_nodes(pf_xml_element_t* entry, int grow_by);

/*********************************************************************************************
Function Name:  delete_text
Description  :  Delete the text object
Inputs       :
Outputs      :  
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static void delete_text(pf_xml_text_t* apoText);

/*********************************************************************************************
Function Name:  delete_clear 
Description  :  Delete the clear object
Inputs       :
Outputs      :  
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static void delete_clear(struct pf_xml_clear* clear);

/*********************************************************************************************
Function Name:  delete_attribute
Description  :  Delete the attribute object
Inputs       :
Outputs      :  
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static void delete_attribute(pf_xml_attribute_t* entry);

/*********************************************************************************************
Function Name:  delete_node
Description  :  Delete the common node
Inputs       :
Outputs      :  
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static void delete_node(pf_xml_node_t* entry);

/*********************************************************************************************
Function Name:  destroy_declare
Description  :  destroy the declare of the xml
Inputs       :
Outputs      :  return value            :  0 
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int destroy_declare(pf_xml_declaration_t* declare);

/*********************************************************************************************
Function Name:  enum_nodes
Description  :  Enum all nodes of the current element
Inputs       :
Outputs      :  return value            :   The pointer of pf_xml_node 
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static pf_xml_node_t* enum_nodes(pf_xml_element_t* entry, int* idx);

/*********************************************************************************************
Function Name:  create_xmlstring_r
Description  :  Create the xmlstring or get the xmlstring 's len
Inputs       :
Outputs      :  return value            :   the xmlstring 's len
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int create_xmlstring_r(pf_xml_element_t* entry, char* marker, int format);

/*********************************************************************************************
Function Name:  create_declarstr
Description  :  Create the declarstr or the len
Inputs       :
Outputs      :  return value            :  the declarstr 's len 
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int create_declarstr(pf_xml_declaration_t* declar, char* result, int format);

/*********************************************************************************************
Function Name:  insert_element_impl
Description  :  Insert a new element under the entry
Inputs       :
Outputs      :  return value            :   The pointer of new element 
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static pf_xml_element_t* insert_element_impl(pf_xml_element_t* entry, 
                                             const char* name, 
                                             int need_malloc);

/*********************************************************************************************
Function Name:  insert_text_impl 
Description  :  Insert a new text by the element 
Inputs       :
Outputs      :  return value            :   The pointer of new text
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static pf_xml_text_t* insert_text_impl(pf_xml_element_t* entry, 
                                       const char* value, 
                                       int need_malloc);

/*********************************************************************************************
Function Name:  insert_attribute_impl
Description  :  Insert a new attribute by the element
Inputs       :
Outputs      :  return value            :   The pointer of new attribute
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static pf_xml_attribute_t* insert_attribute_impl(pf_xml_element_t* entry, 
                                                 const char* name, 
                                                 const char* value, 
                                                 int need_malloc);

/*********************************************************************************************
Function Name:  get_declare_attr
Description  :  Get the declare_attr
Inputs       :
Outputs      :  return value            :  0 
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int get_declare_attr(const char* buff, char* version, char* encoding, char* standalone);

/*********************************************************************************************
Function Name:  parse_xml_delcare
Description  :  Parse the xml declare string 
Inputs       :
Outputs      :  return value            :  0 
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int parse_xml_delcare(const char* bufferstr, pf_xml_t* xml);

/*********************************************************************************************
Function Name:  set_xml_buffer
Description  :  Set the xml_buffer and get the declare string
Inputs       :
Outputs      :  return value            :  
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int set_xml_buffer(pf_xml_buffer_t* xml_buffer, 
                          const char* xmlstr, 
                          char* buffer_declare, 
                          int buffer_size);

#ifdef DEBUG
/*********************************************************************************************
Function Name:  get_error
Description  :  Get the error string from the err_code
Inputs       :
Outputs      :  return value            :   error string
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static const char* get_error(pf_xml_error_e err_code);
#endif /* #ifdef DEBUG */

/*********************************************************************************************
Function Name:  parse_xmlstr
Description  :  Parse the xml string 
Inputs       :
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int parse_xmlstr(pf_xml_buffer_t* xml_buffer, pf_xml_element_t* element, pf_xml_t* xml);

/*********************************************************************************************
Function Name:  get_next_char
Description  :  Get the next char from the xml buffer
Inputs       :
Outputs      :  return value            :   The next char
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static char get_next_char(pf_xml_buffer_t* xml);

/*********************************************************************************************
Function Name:  find_non_whitespace
Description  :  Find the non whitespace char
Inputs       :
Outputs      :  return value            :   The non_whitespace char
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static char find_non_whitespace(pf_xml_buffer_t* xml);

/*********************************************************************************************
Function Name:  pf_xml_strdup
Description  :  strdup from the data by size
Inputs       :
Outputs      :  return value            :  The pointer of new string 
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static char* pf_xml_strdup(const char* data, int size);

/*********************************************************************************************
Function Name:  get_next_token
Description  :  Get the next token
Inputs       :
Outputs      :  return value            :   The next token
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static pf_xml_next_token_t get_next_token(pf_xml_buffer_t* xml, 
                                          int* token_len, 
                                          pf_xml_token_type_e* type);

/*********************************************************************************************
Function Name:  add_clear_impl
Description  :  Add a new clear by the element
Inputs       :
Outputs      :  return value            :   The pointer of clear object
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static struct pf_xml_clear* add_clear_impl(pf_xml_element_t* entry, 
                                           char* value, 
                                           pf_xml_clear_tag_t* clear, 
                                           int need_malloc);

/*********************************************************************************************
Function Name:  find_end_of_text
Description  :  Find the end of text
Inputs       :
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static void find_end_of_text(const char* token, int* text);

/*********************************************************************************************
Function Name:  parse_clear_tag
Description  :  Parse the clear obj to string
Inputs       :
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int parse_clear_tag(pf_xml_buffer_t* xml, 
                           pf_xml_element_t* element, 
                           pf_xml_clear_tag_t* clear);

/*********************************************************************************************
Function Name:  erase_element
Description  :  Erase the current element
Inputs       :
Outputs      :  return value            :   
ErrorCodes   :
History      :
---------------------------------------------------------------------------------------------
Author                 Date                          Comments
zhanggp                2011-06-23                    create
**********************************************************************************************/
static int erase_element(pf_xml_element_t* element);

/*********************************************************************************************/

pf_xml_t* pf_xml_create(const char* root)
{
    pf_xml_t* xml = malloc(sizeof(pf_xml_t));
    if(!xml)
    {
        pf_set_errno(PF_ENOMEM);
        return NULL;
    }
    xml->decalare = NULL;
    xml->root = malloc(sizeof(pf_xml_element_t));
    if(!xml->root)
    {
        pf_set_errno(PF_ENOMEM);
        return NULL;
    }
    init_element(xml->root, root, 1);

    return xml;
}

int pf_xml_destroy(pf_xml_t* xml)
{
    assert(xml);

    if(xml->decalare)
    {
        destroy_declare(xml->decalare);
    }
    if(xml->root)
    {
        erase_element(xml->root);
        xml->root = NULL;
    }
    free(xml);

    return 0;
}

int pf_xml_clearup(pf_xml_t* xml)
{
    pf_xml_element_t* element = NULL;
    pf_xml_clear_t*   clear   = NULL;
    pf_xml_element_t* root    = NULL;
    int               idx     = 0;
    int               c_idx   = 0;
    assert(xml);

    root = pf_xml_get_root(xml);
    if(root)
    {
        while((element = pf_xml_enum_elements(root, &idx)) != NULL)
        {
            erase_element(element);
            element = NULL;
        }
        while((clear = enum_clear(root, &c_idx)) != NULL)
        {
            delete_clear(clear);
            clear = NULL;
        }

        root->count = 0;
        root->max = 0;
    }

    return 0;
}

pf_xml_element_t* pf_xml_get_root(pf_xml_t* xml)
{
    pf_xml_element_t* enum_entry = NULL;
    int               idx        = 0;
    int               idx_tmp    = 0;

    assert(xml);

    if(strcmp(xml->root->name, "") == 0)
    {
        while((enum_entry = pf_xml_enum_elements(xml->root, &idx)))
        {
            if(++idx_tmp > 1)
            {
                break;
            }
        }
        if(idx_tmp == 1)
        { 
            return xml->root->entries[idx-1].node.element;
        }
        else if(idx_tmp == 0)
        {
            return NULL;
        }
    }

    return xml->root;
}

char* pf_xml_attrib_get_name(pf_xml_attribute_t* attrib)
{
    assert(attrib);

    return attrib->name;
}

char* pf_xml_attrib_get_value(pf_xml_attribute_t* attrib)
{
    assert(attrib);

    return attrib->value;
}

char* pf_xml_elem_get_name(pf_xml_element_t* element)
{
    assert(element);

    return element->name;
}

char* pf_xml_elem_get_text(pf_xml_element_t* element)
{
    int            idx    = 0;
    pf_xml_text_t* text   = NULL;

    assert(element);

    text = enum_text(element, &idx);
    if(text)
    {
        return text->value;
    }

    return NULL;
}

int pf_xml_init_declare(pf_xml_t* xml, 
                        const char* version, 
                        const char* encoding, 
                        const char* standalone)
{
    assert(xml);

    if(!xml->decalare)
    {
        xml->decalare = malloc(sizeof(pf_xml_declaration_t));
        if(!xml->decalare)
        {
            pf_set_errno(PF_ENOMEM);
            return PF_ENOMEM;
        }
        xml->decalare->version = NULL;
        xml->decalare->encoding = NULL;
        xml->decalare->standalone = NULL;
    }

    if(version && strlen(version) > 0)
    {
        xml->decalare->version = malloc(sizeof(pf_xml_attribute_t));
        if(!xml->decalare->version)
        {
            pf_set_errno(PF_ENOMEM);
            return PF_ENOMEM;
        }
        xml->decalare->version->name = strdup(VERSION);
        xml->decalare->version->value = strdup(version);
    }
    if(encoding && strlen(encoding) > 0)
    {
        xml->decalare->encoding = malloc(sizeof(pf_xml_attribute_t));
        if(!xml->decalare->encoding)
        {
            pf_set_errno(PF_ENOMEM);
            return PF_ENOMEM;
        }
        xml->decalare->encoding->name = strdup(ENCODING);
        xml->decalare->encoding->value = strdup(encoding);
    }
    if(standalone && strlen(standalone) > 0)
    {
        xml->decalare->standalone = malloc(sizeof(pf_xml_attribute_t));
        if(!xml->decalare->standalone)
        {
            pf_set_errno(PF_ENOMEM);
            return PF_ENOMEM;
        }
        xml->decalare->standalone->name = strdup(STANDALONE);
        xml->decalare->standalone->value = strdup(standalone);
    }

    return 0;
}

char* pf_xml_get_version(pf_xml_t* xml)
{
    assert(xml);

    if(xml->decalare && xml->decalare->version)
    {
        return (char*)xml->decalare->version->value;
    }

    return NULL;
}

char* pf_xml_get_encoding(pf_xml_t* xml)
{
    assert(xml);
    
    if(xml->decalare && xml->decalare->encoding)
    {
        return (char*)xml->decalare->encoding->value;
    }

    return NULL;
}

char* pf_xml_get_standalone(pf_xml_t* xml)
{
    assert(xml);

    if(xml->decalare && xml->decalare->standalone)
    {
        return (char*)xml->decalare->standalone->value;
    }

    return NULL;
}

pf_xml_element_t* pf_xml_insert_element(pf_xml_element_t* entry, const char* name)
{
    assert(entry);
    assert(name);

    return insert_element_impl(entry, name, 1);
}

pf_xml_text_t* pf_xml_insert_text(pf_xml_element_t* element, const char* value)
{
    assert(element);
    assert(value);

    return insert_text_impl(element, value, 1);
}

pf_xml_attribute_t* pf_xml_insert_attribute(pf_xml_element_t* element, 
                                            const char* name, 
                                            const char* value)
{
    assert(element);
    assert(value);
    assert(name);

    return insert_attribute_impl(element, name, value, 1);
}

pf_xml_clear_t* pf_xml_insert_cdata(pf_xml_element_t* entry, const char* name)
{
    assert(entry);
    assert(name);

#define GETCDATA()  &get_clear_tags()[0]
    return  add_clear_impl(entry, (char*)name, GETCDATA(), 1);
}

pf_xml_t* pf_xml_parse(const char* xmlstr)
{
    pf_xml_error_e  err_code;
    pf_xml_t*       xml                 = NULL;
    pf_xml_buffer_t init_xml_buffer     = {NULL, 0, PF_XML_ERR_NONE, NULL, 0, NULL, 0, 1, NULL};
    char            buffer_declare[300] = {0,};

    assert(xmlstr);

    if(set_xml_buffer(&init_xml_buffer, xmlstr, buffer_declare, sizeof(buffer_declare) - 1) != 0)
    {
#ifdef DEBUG
        fprintf(stderr, "set_xml_buffer error: %s\n", xmlstr);
#endif
        pf_set_errno(PF_EINVAL);
        return NULL;
    }

    xml = pf_xml_create("");
    if(strlen(buffer_declare) > 7)
    {
        if(parse_xml_delcare(buffer_declare, xml) != 0)
        {
            return NULL;
        }
    }
    parse_xmlstr(&init_xml_buffer, xml->root, xml);

    err_code = init_xml_buffer.err_code;

    if(err_code != PF_XML_ERR_NONE)
    {
        if(xml)
        {
            pf_xml_destroy(xml);
        }
#ifdef DEBUG
        fprintf(stderr, "parse xml error : %s, xmlstr=%s\n", get_error(err_code), xmlstr);
#endif
        pf_set_errno(PF_EINVAL);
        return NULL;
    }

    return xml;
}

char* pf_xml_dump(pf_xml_t* xml, int format)
{
    int         len         = 0;
    int         declar_len  = 0;
    char*       result      = NULL;

    assert(xml);

    if(xml->root)
    {
        format = (format ? 0 : -1);

        len = create_xmlstring_r(xml->root, NULL, format);
        declar_len = create_declarstr(xml->decalare, NULL, format);
        len += declar_len;
        result = (char*)malloc(len + 1);
        if(!result)
        {
            pf_set_errno(PF_ENOMEM);
            return NULL;
        }
        create_declarstr(xml->decalare, result, format);
        create_xmlstring_r(xml->root, result + declar_len, format);
        return  result;
    }

    return  NULL;
}


pf_xml_element_t* pf_xml_enum_elements(pf_xml_element_t* entry, int* idx)
{   
    int                 my_index  = 0;
    pf_xml_element_t*   result    = NULL;

    assert(idx);
    assert(entry);

    my_index = *idx;
    for(; my_index < entry->count && !result; my_index++)
    {
        if(entry->entries[my_index].type == PF_XML_NODE_ELEMENT)
        {
            result = entry->entries[my_index].node.element;
        }
    }

    *idx = my_index;

    return result;
}

pf_xml_attribute_t* pf_xml_enum_attributes(pf_xml_element_t* entry, int* idx)
{   
    int                     my_index = 0;
    pf_xml_attribute_t*     result   = NULL;

    assert(idx);
    assert(entry);

    my_index = *idx;
    for(; my_index < entry->count && !result; my_index++)
    {
        if(entry->entries[my_index].type == PF_XML_NODE_ATTRIBUTE)
        {
            result = entry->entries[my_index].node.attrib;
        }
    }
    *idx = my_index;

    return result;
}

pf_xml_element_t* pf_xml_find_element(pf_xml_element_t* entry, const char* path)
{
    int                 my_index        = 0;
    int                 name_length     = 0;
    int                 com_result      = 0;
    const char*         my_name         = NULL;
    const char*         my_next         = NULL;
    pf_xml_element_t*   my_child        = NULL;

    assert(path);
    assert(entry);

    my_next = strchr(path, '/');
    if(my_next == NULL)
    {
        name_length = strlen(path);     
    }
    else
    {
        name_length = my_next - path;

        if(my_next[1])
        {
            my_next++;
        }
        else
        {
            my_next = NULL;
        }
    }

    if(name_length)
    {
        my_index = 0;
        while((my_child = pf_xml_enum_elements(entry, &my_index)) != NULL)
        {
            my_name = my_child->name;

            if(my_name)
            {
                if(strchr(path, '/') == NULL)
                {
                    com_result = strcmp(path, my_name);
                }
                else
                {
                    com_result = strncmp(path, my_name, name_length);
                }

                if(com_result == 0)
                {
                    if(my_next == NULL)
                    {
                        return my_child;
                    }
                    else 
                    {
                        return  pf_xml_find_element(my_child, my_next);
                    }
                }
            }
        } /* while */
    }

    return NULL;
}

pf_xml_attribute_t* pf_xml_find_attribute(pf_xml_element_t* entry, const char* attrib)
{
    int                     my_index        = 0;
    int                     attrib_length   = 0;
    pf_xml_attribute_t*     my_attrib       = NULL;

    assert(entry);
    assert(attrib);

    my_index = 0;
    attrib_length = strlen(attrib);

    while((my_attrib = pf_xml_enum_attributes(entry, &my_index)) != NULL)
    {
        if(strncmp(my_attrib->name, attrib, attrib_length) == 0)
        {
            return my_attrib;
        }
    }

    return NULL;
}

/*********************************************************************************************/

static pf_xml_clear_tag_t*  get_clear_tags(void)
{
    static pf_xml_clear_tag_t tags[] =
    {
        /* open         close     */
        { "<![CDATA[",  "]]>"       },
        { "<PRE>",      "</PRE>"    },
        { "<Script>",   "</Script>" },
        { "<!--",       "-->"       },
        { "<!DOCTYPE",  ">"         },
        { "<?",         "?>"        },
        { NULL,         NULL        }
    };

    return  tags;
}

static pf_xml_text_t* enum_text(pf_xml_element_t* entry, int* idx)
{   
    int             my_index  = 0;
    pf_xml_text_t*  result    = NULL;

    my_index =* idx;
    for(; my_index < entry->count && !result; my_index++)
    {
        if(entry->entries[my_index].type == PF_XML_NODE_TEXT)
        {
            result = entry->entries[my_index].node.text;
        }
    }

    *idx = my_index;
    return result;
}

static pf_xml_clear_t* enum_clear(pf_xml_element_t* entry, int* idx)
{   
    int             my_index  = 0;
    pf_xml_clear_t* result    = NULL;

    my_index =* idx;
    for(; my_index < entry->count && !result; my_index++)
    {
        if(entry->entries[my_index].type == PF_XML_NODE_CLEAR)
        {
            result = entry->entries[my_index].node.clear;
        }
    }

    *idx = my_index;

    return result;
}

static void init_element(pf_xml_element_t* entry, const char* name, int need_malloc)
{
    entry->max      = 0;
    entry->count    = 0;
    entry->entries  = NULL;
    entry->name     = need_malloc ? strdup(name): (char*)name;

    return;
}

static void attach_nodes(pf_xml_node_t* dst, pf_xml_node_t* src, int num)
{
    int i = 0;

    for(i = 0; i < num; i++)
    {
        dst[i] = src[i];
        src[i].type = PF_XML_NODE_EMPTY;
    }

    return;
}

static int alloc_nodes(pf_xml_element_t* entry, int grow_by)
{
    int             max         = 0;
    pf_xml_node_t*  new_node    = NULL;

    entry->max += grow_by;
    max = entry->max;

    new_node = (pf_xml_node_t*)malloc(sizeof(pf_xml_node_t)*  max);
    if(!new_node)
    {
        pf_set_errno(PF_ENOMEM);
        return PF_ENOMEM;
    }
    attach_nodes(new_node, entry->entries, entry->count);

    if(entry->entries)
    {
        free(entry->entries);
        entry->entries = NULL;
    }
    entry->entries = new_node;

    return 0;
}

static void delete_text(pf_xml_text_t* apoText)
{
    if(apoText->value)
    {
        free(apoText->value);
        apoText->value = NULL;
    }
    free(apoText);

    return;
}

static void delete_clear(struct pf_xml_clear* clear)
{
    if(clear->value)
    {
        free(clear->value);
        clear->value = NULL;
    }
    free(clear);

    return;
}

static void delete_attribute(pf_xml_attribute_t* entry)
{
    if(entry->name) 
    {
        free(entry->name);
        entry->name = NULL;
    }

    if(entry->value) 
    {
        free(entry->value);
        entry->value = NULL;
    }
    free(entry);

    return;
}

static void delete_node(pf_xml_node_t* entry)
{
    if(entry)
    {
        switch(entry->type)
        {
            case PF_XML_NODE_ATTRIBUTE:
                delete_attribute(entry->node.attrib);
                entry->node.attrib = NULL;
                break;

            case PF_XML_NODE_ELEMENT:
                erase_element(entry->node.element);
                entry->node.element = NULL;
                break;

            case PF_XML_NODE_TEXT:
                delete_text(entry->node.text);
                entry->node.text = NULL;
                break;

            case PF_XML_NODE_CLEAR:
                delete_clear(entry->node.clear);
                entry->node.clear = NULL;
                break;

            default:
                assert(1);
                break;
        }   

        entry->type = PF_XML_NODE_EMPTY;
    }

    return;
}

static int destroy_declare(pf_xml_declaration_t* declare)
{
    if(declare->version)
    {
        delete_attribute(declare->version);
        declare->version = NULL;
    }
    if(declare->encoding)
    {
        delete_attribute(declare->encoding);
        declare->encoding = NULL;
    }
    if(declare->standalone)
    {
        delete_attribute(declare->standalone);
        declare->standalone = NULL;
    }
    free(declare);

    return 0;
}

static pf_xml_node_t* enum_nodes(pf_xml_element_t* entry, int* idx)
{   
    pf_xml_node_t* result = NULL;

    if(*idx < entry->count)
    {
        result = &entry->entries[*idx];
        (*idx)++;
    }

    return result;
}

static int create_xmlstring_r(pf_xml_element_t* entry, char* marker, int format)
{
    int                 idx             = 0;
    int                 len             = 0;
    int                 result          = 0;
    int                 child_format    = 0;
    int                 element_len     = 0;
    int                 has_children    = 0;
    pf_xml_node_t*      child_node      = NULL;
    pf_xml_attribute_t* attrib          = NULL;

    element_len = LENSTR(entry->name);

    if(element_len)
    {
        len = (format == -1 ? 0 : format);

        if(marker)
        {
            if(len)
            {
                memset(marker, INDENTCHAR, len);
            }

            result = len;
            marker[result++] = '<';        
            strcpy(&marker[result], entry->name);

            result += element_len;
            marker[result++] = ' ';        
        }                
        else
        {
            result += (len + element_len + 2);
        }

        idx = 0;
        while((child_node = enum_nodes(entry, &idx)) != NULL)
        {
            switch(child_node->type)
            {
                case PF_XML_NODE_ATTRIBUTE:
                    attrib = child_node->node.attrib;
                    len = LENSTR(attrib->name);

                    if(len)
                    {
                        if(marker)
                        {
                            strcpy(&marker[result], attrib->name);             
                        }

                        result += len;
                        len = LENSTR(attrib->value);

                        if(len)
                        {
                            if(marker)
                            {
                                marker[result] = (char)'=';
                                marker[result + 1] = (char)'\"';
                                strcpy(&marker[result + 2], attrib->value);
                                marker[result + 2 + len] = (char)'\"';
                            }
                            result += (len + 1 + 2);
                        }
                        else
                        {
                            if(marker)
                            {
                                marker[result] = (char)'=';
                                marker[result+1] = (char)'\"';
                                marker[result+2] = (char)'\"';
                            }
                            result += 3;
                        }

                        if(marker)
                        {
                            marker[result] = (char)' ';                              
                        }

                        result++;
                    }
                    break;

                default:
                    has_children = 1;
                    break;
            } /* switch */
        } /* while */

        if(has_children)
        {   
            if(marker)
            {
                marker[result-1] = (char)'>';
            }

            if(format != (-1))
            {
                if(marker)
                {
                    marker[result] = (char)'\n';
                }
                result++;
            }
        }
        else
        {
            result--;
        }
    }

    if(format == (-1))
    {
        child_format = -1;
    }
    else
    {
        if(element_len) 
        {
            child_format = format + 1;
        }
        else
        {
            child_format = format;
        }   
    }

    idx = 0;
    while((child_node = enum_nodes(entry, &idx)) != NULL)
    {
        switch(child_node->type)
        {
            case PF_XML_NODE_TEXT:
                len = LENSTR(child_node->node.text->value);

                if(len)
                {
                    if(format != (-1))
                    {
                        if(marker)
                        {
                            memset(&marker[result], INDENTCHAR, format + 1);
                            strcpy(&marker[result + format + 1], child_node->node.text->value);
                            marker[result + format + 1 + len] = (char)'\n';
                        }

                        result += (len + format + 2);
                    }
                    else
                    {
                        if(marker)
                        {
                            strcpy(&marker[result], child_node->node.text->value);
                        }

                        result += len;
                    }               
                }
                break;

            case PF_XML_NODE_CLEAR:
                len = LENSTR(child_node->node.clear->open_tag);

                if(len)
                {
                    if(format != (-1))
                    {
                        if(marker)
                        {
                            memset(&marker[result], INDENTCHAR, format + 1);
                            strcpy(&marker[result + format + 1], child_node->node.clear->open_tag);

                        }

                        result += (len + format + 1);
                    }
                    else
                    {
                        if(marker)
                        {
                            strcpy(&marker[result], child_node->node.clear->open_tag);
                        }

                        result += len;
                    }               
                }

                len = LENSTR(child_node->node.clear->value);

                if(len)
                {
                    if(marker)
                    {
                        strcpy(&marker[result], child_node->node.clear->value);
                    }

                    result += len;
                }

                len = LENSTR(child_node->node.clear->close_tag);

                if(len)
                {
                    if(marker)
                    {
                        strcpy(&marker[result], child_node->node.clear->close_tag);
                    }

                    result += len;
                }

                if(format != (-1))
                {
                    if(marker)
                    {
                        marker[result] = (char)'\n';
                    }

                    result++;
                }
                break;

            case PF_XML_NODE_ELEMENT:
                result += create_xmlstring_r(child_node->node.element, 
                                             (marker ? marker + result : NULL), child_format);
                break;

            default:
                break;
        } /* switch */
    } /* while */

    if(element_len)
    {
        if(has_children)
        {
            if(marker)
            {
                if(format != (-1))
                {
                    if(format)
                    {
                        memset(&marker[result], INDENTCHAR, format);
                        result += format;
                    }
                }       

                strcpy(&marker[result], "</");        
                result += 2;

                strcpy(&marker[result], entry->name);
                result += element_len;

                if(format == (-1))
                {
                    strcpy(&marker[result], ">");
                    result++;
                }
                else
                {
                    strcpy(&marker[result], ">\n");
                    result += 2;
                }
            }
            else
            {
                if(format != (-1))
                {
                    result += (element_len + 4 + format);
                }
                else
                {
                    result += (element_len + 3);
                }
            }
        }
        else 
        {
            if(marker)
            {
                if(format == (-1))
                {
                    strcpy(&marker[result], "/>"); 
                    result += 2;
                }
                else
                {
                    strcpy(&marker[result], "/>\n"); 
                    result += 3;
                }
            }
            else
            {
                result += (format == -1 ? 2 : 3);
            }
        }
    }

    return result;
}

static int create_declarstr(pf_xml_declaration_t* declar, char* result, int format)
{
    int len = 0;
    int len_tmp = 0;

    /*<?xml version="1.0" encoding="ISO-8859-1"?>*/
    if(declar)
    {
        if(result)
        {
            strncpy(result, "<?xml", 5);
            result += 5;
        }
        len += 5;
        
        if(declar->version)
        {
            len_tmp = (LENSTR(declar->version->name) 
                       + LENSTR(declar->version->value) + 2 + 1 + 1);
            if(result)
            {
                sprintf(result, " %s=\"%s\"", declar->version->name, declar->version->value);
                result += len_tmp;
            }
            len += len_tmp;
        }
        if(declar->encoding)
        {
            len_tmp = (LENSTR(declar->encoding->name) 
                       + LENSTR(declar->encoding->value) + 2 + 1 + 1);
            if(result)
            {
                sprintf(result, " %s=\"%s\"", declar->encoding->name, declar->encoding->value);
                result += len_tmp;
            }
            len += len_tmp;
        }
        if(declar->standalone)
        {
            len_tmp = (LENSTR(declar->standalone->name) 
                       + LENSTR(declar->standalone->value) + 2 + 1 + 1);
            if(result)
            {
                sprintf(result, " %s=\"%s\"", declar->standalone->name, declar->standalone->value);
                result += len_tmp;
            }
            len += len_tmp;
        }

        if(result)
        {
            strncpy(result, "?>", 2);
            result += 2;
        }
        len += 2;

        if(format == 0)
        {   
            if(result)
            {
                *(result++) = '\n';
            }
            len++;
        } 
    }

    return len;
}

static pf_xml_element_t* insert_element_impl(pf_xml_element_t* entry, 
                                             const char* name, 
                                             int need_malloc)
{
    pf_xml_node_t*      node        = NULL;
    pf_xml_element_t*   element     = NULL;

    if(entry->count == entry->max)
    {
        if(alloc_nodes(entry, GROWBY) != 0)
        {
            return NULL;
        }
    }

    node = &entry->entries[entry->count];
    node->type = PF_XML_NODE_ELEMENT;

    element = malloc(sizeof(pf_xml_element_t));
    if(!element)
    {
        pf_set_errno(PF_ENOMEM);
        return NULL;
    }
    init_element(element, name, need_malloc);

    node->node.element = element;
    entry->count++;

    return element;
}

static pf_xml_text_t* insert_text_impl(pf_xml_element_t* entry, 
                                       const char* value, 
                                       int need_malloc)
{
    int                 idx    = 0;
    pf_xml_node_t*      node   = NULL;
    pf_xml_text_t*      text   = NULL;

    if(enum_text(entry, &idx))
    {
        pf_set_errno(PF_EKEYREJECTED);
        return NULL;
    }

    if(entry->count == entry->max)
    {
        if(alloc_nodes(entry, GROWBY) != 0)
        {
            return NULL;
        }
    }

    node = &entry->entries[entry->count];
    node->type = PF_XML_NODE_TEXT;

    text = malloc(sizeof(pf_xml_text_t));
    if(!text)
    {
        pf_set_errno(PF_ENOMEM);
        return NULL;
    }
    text->value = need_malloc ? strdup(value) : (char*)value;

    node->node.text = text;
    entry->count++;

    return text;
}

static pf_xml_attribute_t* insert_attribute_impl(pf_xml_element_t* entry, 
                                                 const char* name, 
                                                 const char* value, 
                                                 int need_malloc)
{
    pf_xml_node_t*      node         = NULL;
    pf_xml_attribute_t* attrib       = NULL;

    if(entry->count == entry->max)
    {
        if(alloc_nodes(entry, GROWBY) != 0)
        {
            return NULL;
        }
    }

    node = &entry->entries[entry->count];
    node->type = PF_XML_NODE_ATTRIBUTE;

    attrib = malloc(sizeof(pf_xml_attribute_t));
    if(!attrib)
    {
        pf_set_errno(PF_ENOMEM);
        return NULL;
    }
    attrib->value = need_malloc ? strdup(value) : (char*)value;
    attrib->name = need_malloc ? strdup(name) : (char*)name;

    node->node.attrib = attrib;
    entry->count++;

    return attrib;
}

static int get_declare_attr(const char* buff, char* version, char* encoding, char* standalone)
{
    int     num             = 0;
    char    name[50 + 1]    = {0,};
    char    value[50 + 1]   = {0,};

    if(strlen(buff))
    {
        if((num = sscanf(buff, "%50[^=]=%*[\"]%50[^\"]", name, value)) == 2)
        {
            if(strcmp(name, VERSION) == 0)
            {
                strcpy(version, value);
            }
            else if(strcmp(name, ENCODING) == 0)
            {
                strcpy(encoding, value);
            }
            else if(strcmp(name, STANDALONE) == 0)
            {
                strcpy(standalone, value);
            }
        }
    }
    
    return 0;
}

static int parse_xml_delcare(const char* bufferstr, pf_xml_t* xml)
{
    char version[100 + 1]       = {0,};
    char encoding[100 + 1]      = {0,};
    char standalone[100 + 1]    = {0,};
    char buf1[110 + 1]          = {0,};
    char buf2[110 + 1]          = {0,};
    char buf3[110 + 1]          = {0,};
    

    sscanf(bufferstr, "<?xml %100s %100s %100s?>", buf1, buf2, buf3);
    get_declare_attr(buf1, version, encoding, standalone);
    get_declare_attr(buf2, version, encoding, standalone);
    get_declare_attr(buf3, version, encoding, standalone);

    return pf_xml_init_declare(xml, version, encoding, standalone);
}

static int set_xml_buffer(pf_xml_buffer_t* xml_buffer, 
                          const char* xmlstr, 
                          char* buffer_declare, 
                          int buffer_size)
{
    int     copy_len = 0;
    char*   start    = NULL;
    char*   end      = NULL;

    xml_buffer->clear_tags = get_clear_tags();
    start = strstr(xmlstr, "<?xml");
    if(start)
    {
        end = strstr(start, "?>");
        if(end)
        {
            copy_len = end - start + 2;
            if(copy_len >= buffer_size)
            {
                return  -1;
            }

            xml_buffer->xml = end + 2;
            strncpy(buffer_declare, start, copy_len);
            return 0;
        }
    }

    xml_buffer->xml = xmlstr;
    return 0;
}

#ifdef DEBUG
static const char* get_error(pf_xml_error_e err_code)
{
    int         it        = 0;
    const char* error_str = "Unknown";

    typedef struct error_list
    {
        pf_xml_error_e      err_code;
        const char*         err_str;
    } error_list_t;

    static error_list_t error_list[] = 
    {
        { PF_XML_ERR_NONE,              "No error"                  },
        { PF_XML_ERR_EMPTY,             "No XML data"               },
        { PF_XML_ERR_FIRST_NOT_START_TAG,  "First token not start tag" },
        { PF_XML_ERR_MISS_TAG_NAME,    "Missing start tag name"    },
        { PF_XML_ERR_MISS_END_TAG_NAME, "Missing end tag name"      },
        { PF_XML_ERR_UNMATCH_QUOTE,   "Unmatched quote"           },
        { PF_XML_ERR_UNMATCH_END_TAG,   "Unmatched end tag"         },
        { PF_XML_ERR_UNEXPECTED_TOKEN,   "Unexpected token found"    },
        { PF_XML_ERR_INVALID_TAG,        "Invalid tag found"         },
        { PF_XML_ERR_NO_ELEMENTS,        "No elements found"         },
        { PF_XML_ERR_INVALID_ATTR,        "attribute error"         },
        { PF_XML_ERR_NONE,              NULL                        }
    };

    for(it = 0; error_list[it].err_str; it++)
    {
        if(error_list[it].err_code == err_code)
        {
            error_str = error_list[it].err_str;
            break;
        }
    }

    return error_str;    
}
#endif /* #ifdef DEBUG */

static int parse_xmlstr(pf_xml_buffer_t* xml_buffer, pf_xml_element_t* element, pf_xml_t* xml)
{
    int             temp_len    = 0;
    int             token_len   = 0;
    char*           dup_str    = NULL;
    const char*     temp_str         = NULL;
    const char*     text_str         = NULL;
    const char*     token_str        = NULL;

    pf_xml_status_e     status;
    pf_xml_next_token_t token_t;
    pf_xml_token_type_e token_type;
    pf_xml_element_t*   new_element_t = NULL;
    pf_xml_attrib_e     attrib_type   = PF_XML_ATTRIB_NAME;

    if(xml_buffer->first)
    {
        xml_buffer->first = 0;
        status = PF_XML_STATUS_OUTSIDE_TAG;
    }
    else 
    {
        status = PF_XML_STATUS_INSIDE_TAG;
    }

    while(1)
    {
        token_t = get_next_token(xml_buffer, &token_len, &token_type);

        if(token_type == PF_XML_TOKEN_ERROR)
        {
            return 0;
        }

        if(status == PF_XML_STATUS_OUTSIDE_TAG)
        {
            switch(token_type)
            {
                case PF_XML_TOKEN_TEXT:
                case PF_XML_TOKEN_QUOTED_TEXT:
                case PF_XML_TOKEN_EQUALS:
                    if(text_str == NULL)
                    {
                        text_str = token_t.str_begin;
                    }                   
                    break;              

                case PF_XML_TOKEN_TAG_START:
                    if(text_str)
                    {
                        xml_buffer->err_code = PF_XML_ERR_UNEXPECTED_TOKEN; 
                        return 0;
                    }

                    token_t = get_next_token(xml_buffer, &token_len, &token_type);
                    if(token_type != PF_XML_TOKEN_TEXT)
                    {
                        xml_buffer->err_code = PF_XML_ERR_MISS_TAG_NAME;
                        return 0;
                    }

                    dup_str = pf_xml_strdup(token_t.str_begin, token_len);
                    new_element_t = insert_element_impl(element, dup_str, 0);

                    while(new_element_t)
                    {
                        if(parse_xmlstr(xml_buffer, new_element_t, xml) == 0)
                        {
                            return 0;
                        }

                        new_element_t = NULL;
                    }
                    break;

                case PF_XML_TOKEN_TAG_END:
                    if(text_str)
                    {
                        temp_len = token_t.str_begin - text_str;
                        find_end_of_text(text_str, &temp_len);

                        dup_str = pf_xml_strdup(text_str, temp_len);
                        insert_text_impl(element, dup_str, 0);
                        text_str = NULL;
                    }

                    token_t = get_next_token(xml_buffer, &temp_len, &token_type);                  
                    if(token_type != PF_XML_TOKEN_TEXT)
                    {
                        xml_buffer->err_code = PF_XML_ERR_MISS_END_TAG_NAME;
                        return 0;
                    }

                    temp_str = token_t.str_begin;
                    token_t = get_next_token(xml_buffer, &token_len, &token_type);

                    if(token_type != PF_XML_TOKEN_CLOSE_TAG)
                    {
                        xml_buffer->err_code = PF_XML_ERR_MISS_END_TAG_NAME;
                        return 0;
                    }

                    if(element->name == NULL)
                    {
                        break;
                    }

                    if(strncmp(temp_str, element->name, temp_len))
                    {
                        xml_buffer->err_code = PF_XML_ERR_UNMATCH_END_TAG;
                        return 0;
                    }
                    
                    return 1;

                case PF_XML_TOKEN_CLEAR:
                    if(text_str)
                    {
                        xml_buffer->err_code = PF_XML_ERR_UNEXPECTED_TOKEN; 
                        return 0;
                    }

                    if(parse_clear_tag(xml_buffer, element, token_t.clear) == 0)
                    {
                        return 0;
                    }
                    break;

                case PF_XML_TOKEN_CLOSE_TAG:        /* '>'   */
                case PF_XML_TOKEN_SHORT_HAND_CLOSE: /* '/>'  */
                    xml_buffer->err_code = PF_XML_ERR_UNEXPECTED_TOKEN;
                    return 0;

                case PF_XML_TOKEN_ERROR:
                default:
                    break;
            } /* switch */
        }
        else if(status == PF_XML_STATUS_INSIDE_TAG)
        {
            switch(attrib_type)
            {
                case PF_XML_ATTRIB_NAME:
                    switch(token_type)
                    {
                        case PF_XML_TOKEN_TEXT:
                            temp_str = token_t.str_begin;
                            temp_len = token_len;
                            attrib_type = PF_XML_ATTRIB_EQUALS;
                            break;

                        case PF_XML_TOKEN_CLOSE_TAG:
                            status = PF_XML_STATUS_OUTSIDE_TAG;
                            break;

                        case PF_XML_TOKEN_SHORT_HAND_CLOSE:
                            return 1;

                        case PF_XML_TOKEN_QUOTED_TEXT:  /* '"SomeText"' */
                        case PF_XML_TOKEN_TAG_START:    /* '<'          */
                        case PF_XML_TOKEN_TAG_END:      /* '</'         */
                        case PF_XML_TOKEN_EQUALS:       /* '='          */
                        case PF_XML_TOKEN_DECLARATION:  /* '<?'         */
                        case PF_XML_TOKEN_CLEAR:
                            xml_buffer->err_code = PF_XML_ERR_UNEXPECTED_TOKEN;
                            return 0;

                        case PF_XML_TOKEN_ERROR:
                        default:
                            break;
                    }
                    break;

                case PF_XML_ATTRIB_EQUALS:
                    switch(token_type)
                    {
                        case PF_XML_TOKEN_EQUALS:
                            attrib_type = PF_XML_ATTRIB_VALUE;
                            break;
                        default:
                            xml_buffer->err_code = PF_XML_ERR_INVALID_ATTR;
                            return 0;

                    }
                    break;

                case PF_XML_ATTRIB_VALUE:
                    switch(token_type)
                    {
                        case PF_XML_TOKEN_TEXT:
                            xml_buffer->err_code = PF_XML_ERR_INVALID_ATTR;
                            return 0;
                        case PF_XML_TOKEN_QUOTED_TEXT:
                            if(temp_len)
                            {
                                token_str = pf_xml_strdup(token_t.str_begin + 1, token_len - 2);
                            }
                            
                            dup_str = pf_xml_strdup(temp_str, temp_len);
                            
                            insert_attribute_impl(element, dup_str, (char*)token_str, 0);

                            attrib_type = PF_XML_ATTRIB_NAME;
                            break;

                        case PF_XML_TOKEN_TAG_START:        /* 'Attr = <'   */
                        case PF_XML_TOKEN_TAG_END:          /* 'Attr = </'  */
                        case PF_XML_TOKEN_CLOSE_TAG:        /* 'Attr = >'   */
                        case PF_XML_TOKEN_SHORT_HAND_CLOSE: /* "Attr = />"  */
                        case PF_XML_TOKEN_EQUALS:           /* 'Attr = ='   */
                        case PF_XML_TOKEN_DECLARATION:      /* 'Attr = <?'  */
                        case PF_XML_TOKEN_CLEAR:
                            xml_buffer->err_code = PF_XML_ERR_UNEXPECTED_TOKEN;
                            return 0;
                            break;

                        case PF_XML_TOKEN_ERROR:
                        default:
                            break;
                    } /* switch(token_type) */
                    break;
                default: /* should not occur */
                    break;
            } /* switch(attrib_type) */
        }
    } /* while(1) */
}

static char get_next_char(pf_xml_buffer_t* xml)
{
    char my_char = 0;

    my_char = xml->xml[xml->index];
    if(my_char) xml->index++;

    return my_char;
}

static char find_non_whitespace(pf_xml_buffer_t* xml)
{
    char    my_char     = 0;
    int     exit_flag   = 0;

    while((exit_flag == 0) && (my_char = get_next_char(xml)))
    {
        switch(my_char)
        {
            case '\n':
            case ' ':
            case '\t':
            case '\r':
                continue;           

            default:
                exit_flag = 1;
                break;
        }
    }

    return my_char;
}

static char* pf_xml_strdup(const char* data, int size)
{
    int     my_size  = 0;
    char*   tmp      = NULL;

    my_size = size;
    tmp = malloc(my_size + 1);
    
    memcpy(tmp, data, my_size);
    tmp[my_size] = (char)0;

    return tmp;
}

static pf_xml_next_token_t get_next_token(pf_xml_buffer_t* xml, 
                                          int* token_len, 
                                          pf_xml_token_type_e* type)
{
    int             my_size          = 0;
    int             my_index         = 0;
    int             exit_flag        = 0;
    int             found_match      = 0;
    int             oepn_length      = 0;
    int             is_text          = 0;
    char            is_char          = 0;
    char            tmp_char         = 0;
    const char*     my_open          = NULL;
    const char*     my_xml           = NULL;
    pf_xml_next_token_t result_t;

    is_char = find_non_whitespace(xml);
    memset(&result_t, 0, sizeof(pf_xml_next_token_t));

    if(is_char == 0)
    {
        *token_len = 0;
        *type = PF_XML_TOKEN_ERROR;
        return result_t;
    }

    my_xml = xml->xml;
    result_t.str_begin = &my_xml[xml->index-1];

    my_index = 0;
    while(1)
    {
        my_open = xml->clear_tags[my_index].open;
        if(my_open == NULL)
        {
            break;
        }

        oepn_length = strlen(my_open);
        if(strncmp(my_open, result_t.str_begin, oepn_length) == 0)
        {
            result_t.clear = &xml->clear_tags[my_index];
            xml->index += (oepn_length-1);

            *type = PF_XML_TOKEN_CLEAR;
            return result_t;
        }

        my_index++;
    }

    tmp_char = 0;
    my_xml = xml->xml;

    switch(is_char)
    {
        case '\'':
        case '\"':
            *type = PF_XML_TOKEN_QUOTED_TEXT;
            tmp_char = is_char;

            my_size = 1;
            found_match = 0;

            while((is_char = get_next_char(xml)) != 0)
            {
                my_size++;

                if(is_char == tmp_char)
                {
                    found_match = 1;
                    break;
                }
            }

            if(found_match == 0)
            {
                xml->err_code = PF_XML_ERR_UNMATCH_QUOTE;               
                *type = PF_XML_TOKEN_ERROR;
            }

            if(find_non_whitespace(xml))
            {
                xml->index--;
            }

            break;

        case '=':
            my_size = 1;
            *type = PF_XML_TOKEN_EQUALS;
            break;

        case '>':
            my_size = 1;
            *type = PF_XML_TOKEN_CLOSE_TAG;
            break;

        case '<':
            tmp_char = xml->xml[xml->index];

            if(tmp_char == '/')
            {
                get_next_char(xml);
                *type = PF_XML_TOKEN_TAG_END;
                my_size = 2;
            }
            else 
            {
                *type = PF_XML_TOKEN_TAG_START;
                my_size = 1;
            }
            break;

        case '/':
            tmp_char = xml->xml[xml->index];

            if(tmp_char == '>')
            {
                get_next_char(xml);
                *type = PF_XML_TOKEN_SHORT_HAND_CLOSE;
                my_size = 2;
                break;
            }

        default:
            is_text = 1;
            break;
    } /* switch */

    if(is_text)
    {
        my_size = 1;
        exit_flag = 0;
        *type = PF_XML_TOKEN_TEXT;

        while((exit_flag == 0) && (is_char = get_next_char(xml)))
        {
            switch(is_char)
            {
                case '\n':
                case ' ':
                case '\t':
                case '\r':
                    exit_flag = 1;
                    break;

                case '/':
                    tmp_char = xml->xml[xml->index];

                    if(tmp_char == '>')
                    {
                        xml->index--;
                        exit_flag = 1;
                    }
                    else
                    {
                        my_size++;
                    }
                    break;

                case '<':
                case '>':
                case '=':
                    xml->index--;                   
                    exit_flag = 1;
                    break;

                default:
                    my_size++;
                    break;
            } /* switch */
        } /* while */
    } /* if */

    *token_len = my_size;

    return result_t;
}

static struct pf_xml_clear* add_clear_impl(pf_xml_element_t* entry, 
                                           char* value, 
                                           pf_xml_clear_tag_t* clear, 
                                           int need_malloc)
{
    pf_xml_node_t*          node        = NULL;
    struct pf_xml_clear*    new_clear   = NULL;

    if(entry->count == entry->max)
    {
        if(alloc_nodes(entry, GROWBY) != 0)
        {
            return NULL;
        }
    }

    node = &entry->entries[entry->count];
    node->type = PF_XML_NODE_CLEAR;

    new_clear = (struct pf_xml_clear*)malloc(sizeof(struct pf_xml_clear));
    if(!new_clear)
    {
        pf_set_errno(PF_ENOMEM);
        return NULL;
    }
    new_clear->close_tag = clear->close;
    new_clear->open_tag = clear->open;
    new_clear->value = need_malloc ? strdup(value) : (char*)value;

    node->node.clear = new_clear;
    entry->count++;

    return new_clear;
}

static void find_end_of_text(const char* token, int* text)
{
    char        my_char     = 0;
    int         text_length = 0;

    text_length = (*text)-1;    
    while(1)
    {
        my_char = token[text_length];

        switch(my_char)
        {
            case '\r':
            case '\n':
            case '\t':
            case ' ':
                text_length--;
                break;

            default:
                *text = (text_length+1);
                return;
        }
    } /* while */

    return;
}

static int parse_clear_tag(pf_xml_buffer_t* xml, 
                           pf_xml_element_t* element, 
                           pf_xml_clear_tag_t* clear)
{
    int         len = 0;
    char*       tmp_str  = NULL;
    const char* xml_str   = &xml->xml[xml->index];

    tmp_str = strstr(xml_str, clear->close);

    if(tmp_str)
    {
        len = tmp_str - xml_str;

        xml->index += len;
        xml->index += strlen(clear->close);

        tmp_str = pf_xml_strdup(xml_str, len);
        add_clear_impl(element, tmp_str, clear, 0);

        return 1;
    }

    xml->err_code = PF_XML_ERR_UNMATCH_END_TAG;

    return 0;
}

int erase_element(pf_xml_element_t* element)
{
    int                 i       = 0;
    pf_xml_element_t*   entry   = element;

    assert(entry);

    for(i = 0; i < entry->count; i++)
    {
        delete_node(&entry->entries[i]);
    }

    entry->max = 0;
    entry->count = 0;

    free(entry->entries);
    entry->entries = NULL;

    if(entry->name)
    {
        free(entry->name);
        entry->name = NULL;
    }
    free(element);

    return 0;
}
